package com.easyapi.server.service.api.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easyapi.common.base.UserTokenThreadHolder;
import com.easyapi.common.contants.Constants;
import com.easyapi.common.enums.StatusCode;
import com.easyapi.common.enums.api.ApiOperationEnum;
import com.easyapi.common.enums.api.InterfaceInfoEnum;
import com.easyapi.common.enums.datasource.DataSourceType;
import com.easyapi.common.enums.datasource.SqlDriverClassName;
import com.easyapi.common.exception.BizException;
import com.easyapi.common.metadata.SqlParserResult;
import com.easyapi.common.param.api.*;
import com.easyapi.common.utils.*;
import com.easyapi.common.vo.api.ApiInfoPageVo;
import com.easyapi.common.vo.api.ApiInfoVo;
import com.easyapi.common.vo.api.ApiMarketInfo;
import com.easyapi.common.vo.metadata.TableStruct;
import com.easyapi.common.vo.user.ApiCenterUserVo;
import com.easyapi.dao.entity.api.ApiCenterDatasource;
import com.easyapi.dao.entity.api.ApiCenterInterfaceInfo;
import com.easyapi.dao.entity.api.ApiCenterProject;
import com.easyapi.dao.entity.api.ApiCenterProjectDatasource;
import com.easyapi.dao.entity.user.ApiCenterUser;
import com.easyapi.dao.mapper.api.ApiCenterInterfaceInfoMapper;
import com.easyapi.server.config.PoolConfig;
import com.easyapi.server.database.mysql.MySQLMetadata;
import com.easyapi.server.observation.ApiBeObserver;
import com.easyapi.server.service.api.ApiService;
import com.easyapi.server.service.api.DataSourceService;
import com.easyapi.server.service.api.ProjectDatasourceService;
import com.easyapi.server.service.api.ProjectService;
import com.easyapi.server.service.sync.AsyncMethodHandler;
import com.easyapi.server.service.user.UserService;
import com.easyapi.server.utils.CaffeineCacheUtil;
import com.easyapi.server.utils.DruidDataSourceUtil;
import com.easyapi.server.utils.MySQLUtil;
import com.easyapi.server.utils.RedisCache;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author javadadi
 */
@Service
public class ApiServiceImpl extends ServiceImpl<ApiCenterInterfaceInfoMapper, ApiCenterInterfaceInfo> implements ApiService {

    private static final Integer SEARCH_BATH_SIZE = 500;

    @Resource
    private ApiCenterInterfaceInfoMapper apiCenterInterfaceInfoMapper;

    @Resource
    private UserService userService;

    @Resource
    private ProjectService projectService;

    @Resource
    private ProjectDatasourceService projectDatasourceService;

    @Resource
    private DataSourceService dataSourceService;

    @Resource
    private AsyncMethodHandler<ApiCenterInterfaceInfo> asyncMethodHandler;

    @Resource
    private ApplicationEventPublisher eventPublisher;

    @Resource
    private RedisCache redisCache;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ApiBeObserver apiBeObserver;
    /**
     * 配置api 参数生成
     *
     * @param param
     * @return
     */
    public ApiCenterInterfaceInfo buildApiInterfaceInfo(GenerateApiParam param) {
        String apiNameCn = param.getApiNameCn();
        String apiDesc = param.getApiDesc();
        //长度校验
        if (RegularUtil.StrsLengthError(1, 200, apiDesc, param.getApiName(), apiNameCn)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        //corn 表达式
        String cacheExpiryDateCron = param.getCacheExpiryDateCron();
        if (StringUtils.isNotEmpty(cacheExpiryDateCron) && !RegularUtil.isCorn(cacheExpiryDateCron)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        ApiCenterUserVo currentUser = UserTokenThreadHolder.getCurrentUser();
        if (Objects.isNull(currentUser)) {
            throw new BizException(StatusCode.NO_LOGIN);
        }
        ApiCenterInterfaceInfo build = ApiCenterInterfaceInfo.builder()
                .projectId(param.getProjectId())
                .apiNameCn(apiNameCn)
                .apiName(apiNameCn)
                .apiDesc(apiDesc)
                .reqType(param.getReqType())
                .queryTimeOut(param.getQueryTimeOut())
                .reqLimit(param.getReqLimit())
                .isCache(param.getIsCache())
                .cacheExpiryPeriod(param.getCacheExpiryPeriod())
                .cacheExpiryDateCron(cacheExpiryDateCron)
                .datasourceId(param.getDatasourceId())
                .databasesName(param.getDatabaseName())
                .tableName(param.getTableName())
                .allowPaging(param.getAllowPaging())
                .respPageSize(param.getRespPageSize()).build();
        build.setCreateBy(currentUser.getId());
        return build;
    }

    /**
     * 自定义sql 参数生成
     *
     * @param param
     * @return
     */
    public ApiCenterInterfaceInfo buildConfigSqlInterface(ConfigSqlApiParam param) {
        String apiNameCn = param.getApiNameCn();
        String apiDesc = param.getApiDesc();
        //长度校验
        if (RegularUtil.StrsLengthError(1, 200, apiDesc, param.getApiName(), apiNameCn)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        //corn 表达式
        String cacheExpiryDateCron = param.getCacheExpiryDateCron();
        if (StringUtils.isNotEmpty(cacheExpiryDateCron) && !RegularUtil.isCorn(cacheExpiryDateCron)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        //sql 语句基本校验
        String configSql = param.getConfigSql();
        if (StringUtils.isEmpty(configSql)) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        ApiCenterUserVo currentUser = UserTokenThreadHolder.getCurrentUser();
        if (Objects.isNull(currentUser)) {
            throw new BizException(StatusCode.NO_LOGIN);
        }
        ApiCenterInterfaceInfo build = ApiCenterInterfaceInfo
                .builder()
                .projectId(param.getProjectId())
                .apiNameCn(apiNameCn)
                .apiNameCn(param.getApiName())
                .apiDesc(param.getApiDesc())
                .reqType(param.getReqType())
                .queryTimeOut(param.getQueryTimeOut())
                .reqLimit(param.getReqLimit())
                .isCache(param.getIsCache())
                .cacheExpiryPeriod(param.getCacheExpiryPeriod())
                .cacheExpiryDateCron(param.getCacheExpiryDateCron())
                .datasourceId(param.getDatasourceId())
                .databasesName(param.getDatabaseName())
                .configSql(configSql)
                .allowPaging(param.getAllowPaging())
                .respPageSize(param.getRespPageSize())
                .build();
        build.setCreateBy(currentUser.getId());
        build.setUpdateBy(currentUser.getId());
        return build;
    }

    /**
     * 链接获取
     *
     * @param datasource
     * @return
     */
    public DruidDataSource getDruidDataSource(ApiCenterDatasource datasource) {
        String key = datasource.getDataName() + "_" + datasource.getId();
        DruidDataSource druidDataSource = null;
        if (CaffeineCacheUtil.hasCache(key)) {
            druidDataSource = (DruidDataSource) CaffeineCacheUtil.get(key);
        } else {
            druidDataSource = DruidDataSourceUtil.getDruidDataSource(
                    datasource.getDataJson(),
                    datasource.getDatasourceUsername(),
                    datasource.getDatasourcePassword(),
                    SqlDriverClassName.MYSQL.getDriverClassName());
        }
        if (Objects.isNull(druidDataSource)) {
            throw new BizException(StatusCode.LINK_DATASOURCE_FALSE);
        }
        return druidDataSource;
    }

    /**
     * 生成api
     *
     * @param interfaceInfo
     * @param dataSource
     * @param inputParams
     * @param outputParams
     * @return
     */
    public ApiCenterInterfaceInfo generateApi(ApiCenterInterfaceInfo interfaceInfo, DruidDataSource dataSource, List<InputParam> inputParams, List<OutputParam> outputParams) {
        List<String> databases = MySQLMetadata.listAllDatabases(dataSource);
        String databasesName = interfaceInfo.getDatabasesName();
        if (!databases.contains(databasesName)) {
            throw new BizException(StatusCode.NO_SUCH_DATABASE);
        }
        String tableName = interfaceInfo.getTableName();
        List<String> tables = MySQLMetadata.listAllTables(dataSource, databasesName);
        if (!tables.contains(tableName)) {
            throw new BizException(StatusCode.NO_SUCH_TABLE);
        }
        //入参处理
        Map<String, TableStruct> tableStructMap = MySQLMetadata.getTableStruct(dataSource,
                        databasesName,
                        tableName)
                .stream()
                .collect(Collectors.toMap(TableStruct::getColumnName, Function.identity()));
        //入参检查
        if (CollectionUtils.isNotEmpty(inputParams)) {
            for (InputParam inputParam : inputParams) {
                String bindField = inputParam.getBindField();
                if (!tableStructMap.containsKey(bindField)) {
                    throw new BizException(StatusCode.NO_SUCH_FIELD);
                }
                //检查操作符是否合法
                if (!SqlOperatorValidator.isValidOperator(inputParam.getFieldType(), inputParam.getOperator())) {
                    throw new BizException(StatusCode.OPERATOR_ILLEGALITY);
                }
                //必填字段没有默认为0
                if (Objects.isNull(inputParam.getRequired())) {
                    inputParam.setRequired(0);
                }
                //参数名称没有默认为绑定字段名，不报错
                if (StringUtils.isEmpty(inputParam.getParamName())) {
                    inputParam.setParamName(bindField);
                }
            }
            //没有问题就保存到数据库里面
            String inputParamJson = JSONUtil.toJsonStr(inputParams);
            interfaceInfo.setInputParams(inputParamJson);
        }
        //输出参数检查
        if (CollectionUtils.isNotEmpty(outputParams)) {
            for (OutputParam outputParam : outputParams) {
                String bindField = outputParam.getBindField();
                if (!tableStructMap.containsKey(bindField)) {
                    throw new BizException(StatusCode.NO_SUCH_FIELD);
                }
                //字段描述不能够太长
                String extra = outputParam.getExtra();
                if (StringUtils.isNotEmpty(extra) && extra.length() > 244) {
                    throw new BizException(StatusCode.PARAMS_ERROR);
                }
                //参数名称没有默认为绑定字段名，不报错
                if (StringUtils.isEmpty(outputParam.getParamName())) {
                    outputParam.setParamName(bindField);
                }
            }
            //没有问题就保存到数据库里面
            String inputParamJson = JSONUtil.toJsonStr(outputParams);
            interfaceInfo.setOutputParams(inputParamJson);
        } else {
            throw new BizException(StatusCode.NO_OUT_API);
        }
        //todo 自定义告警配置
        return interfaceInfo;
    }

    /**
     * 自定义sql api
     *
     * @param interfaceInfo
     * @param dataSource
     * @param inputParams
     * @return
     */
    public ApiCenterInterfaceInfo configSqlApi(ApiCenterInterfaceInfo interfaceInfo, DruidDataSource dataSource, List<ConfigInputParam> inputParams) {
        //先解析sql
        SqlParserResult sqlParserResult = MySQLUtil.sqlParser(interfaceInfo.getConfigSql());
        if (Objects.isNull(sqlParserResult) || StringUtils.isEmpty(sqlParserResult.getTableName())) {
            throw new BizException(StatusCode.SQL_PARSER_ERROR);
        }
        interfaceInfo.setTableName(sqlParserResult.getTableName());
        //将自定义sql 更新为标准sql
        interfaceInfo.setConfigSql(sqlParserResult.getSql());
        //检查数据表，数据库是否存在
        List<String> databases = MySQLMetadata.listAllDatabases(dataSource);
        String databasesName = interfaceInfo.getDatabasesName();
        if (!databases.contains(databasesName)) {
            throw new BizException(StatusCode.NO_SUCH_DATABASE);
        }
        String tableName = interfaceInfo.getTableName();
        List<String> tables = MySQLMetadata.listAllTables(dataSource, databasesName);
        if (!tables.contains(tableName)) {
            throw new BizException(StatusCode.NO_SUCH_TABLE);
        }
        //入参处理
        Map<String, TableStruct> tableStructMap = MySQLMetadata.getTableStruct(dataSource,
                        databasesName,
                        tableName)
                .stream()
                .collect(Collectors.toMap(TableStruct::getColumnName, Function.identity()));
        //入参检查
        if (CollectionUtils.isNotEmpty(inputParams)) {
            List<InputParam> inputs = new ArrayList<>();
            for (ConfigInputParam inputParam : inputParams) {
                String paramName = inputParam.getParamName();
                if (StringUtils.isEmpty(paramName)) {
                    throw new BizException(StatusCode.PARAMS_ERROR);
                }
                if (!tableStructMap.containsKey(paramName)) {
                    throw new BizException(StatusCode.NO_SUCH_FIELD);
                }
                //检查操作符是否合法
                if (!SqlOperatorValidator.isValidOperator(inputParam.getFieldType(), inputParam.getOperator())) {
                    throw new BizException(StatusCode.OPERATOR_ILLEGALITY);
                }
                //必填字段没有默认为0
                if (Objects.isNull(inputParam.getRequired())) {
                    inputParam.setRequired(0);
                }
                InputParam build = InputParam.builder()
                        .id(UUID.randomUUID().toString().substring(0, 5))
                        .bindField(paramName)
                        .paramName(paramName)
                        .operator(inputParam.getOperator())
                        .fieldType(inputParam.getFieldType())
                        .required(inputParam.getRequired())
                        .build();
                inputs.add(build);
            }
            //没有问题就保存到数据库里面
            String inputParamJson = JSONUtil.toJsonStr(inputs);
            interfaceInfo.setInputParams(inputParamJson);
        }
        //构建输出参数
        Map<String, String> columnMap = sqlParserResult.getColumnMap();
        if (MapUtils.isEmpty(columnMap)) {
            throw new BizException(StatusCode.NO_OUT_API);
        }
        //遍历map,生成一个输出
        List<OutputParam> outputParams = new ArrayList<>();
        for (Map.Entry<String, String> entry : columnMap.entrySet()) {
            OutputParam outputParam = new OutputParam();
            outputParam.setBindField(entry.getKey());
            String value = entry.getValue();
            //没有别名，参数就用字段名
            if (StringUtils.isEmpty(value)) {
                outputParam.setParamName(entry.getKey());
            } else {
                outputParam.setParamName(value);
            }
            outputParams.add(outputParam);
            // todo 检查输出参数
        }
        String outputJson = JSONUtil.toJsonStr(outputParams);
        interfaceInfo.setOutputParams(outputJson);
        return interfaceInfo;
    }

    /**
     * api市场获取api
     */
    @Override
    public BaseResponse<Page<ApiMarketInfo>> getApiMarketInfo(ApiInfoPageParam param) {
        String apiNameCn = param.getApiNameCn();
        Long projectId = param.getProjectId();
        Long datasourceId = param.getDatasourceId();
        Integer apiType = param.getApiType();
        Integer reqType = param.getReqType();
        Integer paramCfgType = param.getParamCfgType();
        Integer dataSrcType = param.getDataSrcType();
        Long size = param.getSize();
        Long current = param.getCurrent();
        Page<ApiCenterInterfaceInfo> apiMarketInfoPage = new Page<>(current, size);
        LambdaQueryWrapper<ApiCenterInterfaceInfo> wrapper = new LambdaQueryWrapper<ApiCenterInterfaceInfo>()
                .eq(ApiCenterInterfaceInfo::getApiStatus, InterfaceInfoEnum.API_STATUS_NORMAL.getCode())
//                .eq(ApiCenterInterfaceInfo::getTestStatus, InterfaceInfoEnum.IS_COMMITED.getCode())
                .like(StringUtils.isNotEmpty(apiNameCn), ApiCenterInterfaceInfo::getApiNameCn, apiNameCn)
                .eq(Objects.nonNull(projectId), ApiCenterInterfaceInfo::getProjectId, projectId)
                .eq(Objects.nonNull(apiType), ApiCenterInterfaceInfo::getApiType, apiType)
                .eq(Objects.nonNull(reqType), ApiCenterInterfaceInfo::getParamCfgType, paramCfgType)
                .eq(Objects.nonNull(dataSrcType), ApiCenterInterfaceInfo::getDataSrcType, dataSrcType);
        Page<ApiCenterInterfaceInfo> infoPage = page(apiMarketInfoPage, wrapper);
        Page<ApiMarketInfo> resultPage = new Page<>(infoPage.getCurrent(), infoPage.getSize(), infoPage.getTotal());
        List<ApiMarketInfo> collect = infoPage.getRecords().stream().map(m -> {
            ApiCenterUser createUser = userService.getById(m.getCreateBy());
            ApiCenterUser updateUser = userService.getById(m.getUpdateBy());
            ApiCenterProject project = projectService.getById(projectId);
            ApiMarketInfo build = ApiMarketInfo.builder()
                    .id(m.getId())
                    .apiNameCn(m.getApiNameCn())
                    .apiDesc(m.getApiDesc())
                    .paramCfgType(m.getParamCfgType())
                    .apiType(m.getApiType())
                    .createUsername(Objects.isNull(createUser) ? "---" : createUser.getUserAccount())
                    .updateUsername(Objects.isNull(updateUser) ? "---" : updateUser.getUserAccount())
                    .projectName(Objects.isNull(project) ? "---" : project.getProjectName())
                    .createAt(m.getCreateAt())
                    .updateAt(m.getUpdateAt()) //todo 总掉用时间
                    .build();
            return build;
        }).collect(Collectors.toList());
        resultPage.setRecords(collect);
        return ResultUtils.success(resultPage);
    }

    /**
     * 分页
     *
     * @param param
     * @return
     */
    @Override
    public BaseResponse<Page<ApiInfoPageVo>> pageApiInfo(ApiInfoPageParam param) {
        if (Objects.isNull(param.getProjectId())) {
            //todo 优化，由于前段的项目与api 做了分割，所以请求的时候有可能projectId 没带上
            return ResultUtils.success(new Page<>(param.getCurrent(), param.getSize()));
        }
        //查询条件
        Long projectId = param.getProjectId();
        Long id = param.getId();
        String apiNameCn = param.getApiNameCn();
        Long datasourceId = param.getDatasourceId();
        Integer apiType = param.getApiType();
        Integer apiStatus = param.getApiStatus();
        Integer reqType = param.getReqType();
        Integer paramCfgType = param.getParamCfgType();
        Integer dataSrcType = param.getDataSrcType();
        Integer testStatus = param.getTestStatus();
        Long size = param.getSize();
        Long current = param.getCurrent();
        Page<ApiCenterInterfaceInfo> queryPage = new Page<>(current, size);
        //查询语句
        LambdaQueryWrapper<ApiCenterInterfaceInfo> wrapper = new LambdaQueryWrapper<ApiCenterInterfaceInfo>()
                .eq(ApiCenterInterfaceInfo::getProjectId, projectId)
                .eq(Objects.nonNull(id), ApiCenterInterfaceInfo::getId, id)
                .like(StringUtils.isNotEmpty(apiNameCn), ApiCenterInterfaceInfo::getApiNameCn, apiNameCn)
                .eq(Objects.nonNull(datasourceId), ApiCenterInterfaceInfo::getDatasourceId, datasourceId)
                .eq(Objects.nonNull(apiType), ApiCenterInterfaceInfo::getApiType, apiType)
                .eq(Objects.nonNull(apiStatus), ApiCenterInterfaceInfo::getApiStatus, apiStatus)
                .eq(Objects.nonNull(reqType), ApiCenterInterfaceInfo::getReqType, reqType)
                .eq(Objects.nonNull(paramCfgType), ApiCenterInterfaceInfo::getParamCfgType, paramCfgType)
                .eq(Objects.nonNull(dataSrcType), ApiCenterInterfaceInfo::getDataSrcType, dataSrcType)
                .eq(Objects.nonNull(testStatus), ApiCenterInterfaceInfo::getTestStatus, testStatus);
        Page<ApiCenterInterfaceInfo> apiPage = page(queryPage, wrapper);
        List<ApiCenterInterfaceInfo> records = apiPage.getRecords();
        List<ApiInfoPageVo> pageVoList = new ArrayList<>(records.size());
        //转换vo
        for (ApiCenterInterfaceInfo api : records) {
            ApiCenterUser createUser = userService.getById(api.getCreateBy());
            String createUserName = Objects.isNull(createUser) ? "" : createUser.getUserName();
            ApiCenterUser updateUser = userService.getById(api.getUpdateBy());
            String updateUserName = Objects.isNull(updateUser) ? "" : updateUser.getUserName();
            ApiInfoPageVo pageVo = ApiInfoPageVo.builder()
                    .id(api.getId())
                    .apiNameCn(api.getApiNameCn())
                    .apiDesc(api.getApiDesc())
                    .projectId(api.getProjectId())
                    .datasourceId(api.getDatasourceId())
                    .apiType(api.getApiType())
                    .apiStatus(api.getApiStatus())
                    .testStatus(api.getTestStatus())
                    .createAt(api.getCreateAt())
                    .updateAt(api.getUpdateAt())
                    .createBy(api.getCreateBy())
                    .createUserName(createUserName)
                    .updateBy(api.getUpdateBy())
                    .updateUserName(updateUserName)
                    .build();
            pageVoList.add(pageVo);
        }
        Page<ApiInfoPageVo> resultPage = new Page<>(apiPage.getCurrent(), apiPage.getSize(), apiPage.getTotal());
        resultPage.setRecords(pageVoList);
        return ResultUtils.success(resultPage);
    }

    /**
     * 生成api
     *
     * @param param
     * @return
     */
    @Override
    public BaseResponse<Boolean> generateInterface(GenerateApiParam param) {
        Long projectId = param.getProjectId();
        Long datasourceId = param.getDatasourceId();
        //查看project是否存在
        ApiCenterProject project = projectService.getById(projectId);
        if (Objects.isNull(project)) {
            throw new BizException(StatusCode.NO_SUCH_PROJECT);
        }
        //查看datasource是否存在
        ApiCenterDatasource datasource = dataSourceService.getById(datasourceId);
        if (Objects.isNull(datasource)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE);
        }
        //查看项目的数据源引用
        List<Long> datasourceIdList = projectDatasourceService.listByProjectId(projectId).stream().map(ApiCenterProjectDatasource::getDatasourceId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(datasourceIdList) || !datasourceIdList.contains(datasourceId)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE_REFERENCE);
        }
        //不允许同项目出现同名api
        String apiName = param.getApiName();
        List<ApiCenterInterfaceInfo> infos = apiCenterInterfaceInfoMapper.listByApiName(apiName, projectId);
        if (BooleanUtils.isFalse(RegularUtil.isApiName(apiName))) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        String path = project + "/" + apiName;
        if (CollectionUtils.isNotEmpty(infos)) {
            throw new BizException(StatusCode.API_ALL_READY_EXIST);
        }
        ApiCenterInterfaceInfo apiCenterInterfaceInfo = buildApiInterfaceInfo(param);
        apiCenterInterfaceInfo.setApiPath(path);
        apiCenterInterfaceInfo.setParamCfgType(InterfaceInfoEnum.PARAM_CFG_TYPE_GENERATOR.getCode());
        apiCenterInterfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT.getCode());
        apiCenterInterfaceInfo.setApiType(InterfaceInfoEnum.API_TYPE_GENERATOR.getCode());
        save(apiCenterInterfaceInfo);
        //异步执行
        PoolConfig.RUN_SYNC_JOB_POOL.submit(() -> {
            ApiCenterInterfaceInfo interfaceInfo = null;
            try {
                DruidDataSource druidDataSource = getDruidDataSource(datasource);
                CompletableFuture<ApiCenterInterfaceInfo> future = asyncMethodHandler.async(() -> generateApi(apiCenterInterfaceInfo, druidDataSource, param.getInputParams(), param.getOutputParams()));
                interfaceInfo = future.get();
                interfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT_SUCCESS.getCode());
            } catch (Exception e) {
                log.error("generate api failed because error:{}", e);
                if (interfaceInfo != null) {
                    interfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT_FAILED.getCode());
                }
            } finally {
                saveOrUpdate(interfaceInfo);
            }
        });
        return ResultUtils.success(true);
    }

    /**
     * 自定义sql
     *
     * @param param
     * @return
     */
    @Override
    public BaseResponse<Boolean> configSqlInterface(ConfigSqlApiParam param) {
        Long projectId = param.getProjectId();
        ApiCenterProject project = projectService.getById(projectId);
        if (Objects.isNull(project)) {
            throw new BizException(StatusCode.NO_SUCH_PROJECT);
        }
        Long datasourceId = param.getDatasourceId();
        //查看datasource是否存在
        ApiCenterDatasource datasource = dataSourceService.getById(datasourceId);
        if (Objects.isNull(datasource)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE);
        }
        //查看项目的数据源引用
        List<Long> datasourceIdList = projectDatasourceService.listByProjectId(projectId).stream().map(ApiCenterProjectDatasource::getDatasourceId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(datasourceIdList) || !datasourceIdList.contains(datasourceId)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE_REFERENCE);
        }
        //不允许同项目出现同名api
        String apiName = param.getApiName();
        //apiName--5--50字符,必须英文

        List<ApiCenterInterfaceInfo> infos = apiCenterInterfaceInfoMapper.listByApiName(apiName, projectId);
        if (CollectionUtils.isNotEmpty(infos)) {
            throw new BizException(StatusCode.API_ALL_READY_EXIST);
        }
        //api-path 生成-->后续注册到handler mapping 里面
        if (BooleanUtils.isFalse(RegularUtil.isApiName(apiName))) {
            throw new BizException(StatusCode.PARAMS_ERROR);
        }
        String path = project.getProjectDesc() + "/" + apiName;
        ApiCenterInterfaceInfo apiCenterInterfaceInfo = buildConfigSqlInterface(param);
        apiCenterInterfaceInfo.setApiPath(path);
        apiCenterInterfaceInfo.setParamCfgType(InterfaceInfoEnum.PARAM_CFG_TYPE_SQL.getCode());
        apiCenterInterfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT.getCode());
        apiCenterInterfaceInfo.setApiType(InterfaceInfoEnum.API_TYPE_GENERATOR.getCode());
        save(apiCenterInterfaceInfo);
        //异步执行sql 校验以及保存目标表，输出字段  todo 联合查询
        //异步执行
        PoolConfig.RUN_SYNC_JOB_POOL.submit(() -> {
            ApiCenterInterfaceInfo interfaceInfo = null;
            try {
                DruidDataSource druidDataSource = getDruidDataSource(datasource);
                CompletableFuture<ApiCenterInterfaceInfo> future = asyncMethodHandler.async(() -> configSqlApi(apiCenterInterfaceInfo, druidDataSource, param.getConfigInputParams()));
                interfaceInfo = future.get();
                interfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT_SUCCESS.getCode());
            } catch (Exception e) {
                log.error("generate api failed because error:{}", e);
                if (interfaceInfo != null) {
                    interfaceInfo.setApiStatus(InterfaceInfoEnum.API_STATUS_INIT_FAILED.getCode());
                }
            } finally {
                saveOrUpdate(interfaceInfo);
            }
        });
        return ResultUtils.success(true);
    }

    /**
     * api详情获取
     *
     * @param id
     * @return
     */
    @Override
    public BaseResponse<ApiInfoVo> getInterfaceInfo(Long id) {
        ApiCenterInterfaceInfo interfaceInfo = getById(id);
        if (Objects.isNull(interfaceInfo)) {
            throw new BizException(StatusCode.NO_SUCH_API);
        }
        //项目
        ApiCenterProject project = projectService.getById(interfaceInfo.getProjectId());
        if (Objects.isNull(project)) {
            throw new BizException(StatusCode.NO_SUCH_PROJECT);
        }
        //数据源
        ApiCenterDatasource datasource = dataSourceService.getById(interfaceInfo.getDatasourceId());
        if (Objects.isNull(datasource)) {
            throw new BizException(StatusCode.NO_SUCH_DATASOURCE);
        }
        //创建人
        Long createBy = interfaceInfo.getCreateBy();
        ApiCenterUser createUser = userService.getById(createBy);
        //更新人
        Long updateBy = interfaceInfo.getUpdateBy();
        ApiCenterUser updateUser = userService.getById(updateBy);
        Integer apiType = interfaceInfo.getApiType();
        InterfaceInfoEnum apiTypeEnum = ApiStatusUtil.getApiType(apiType);
        //api状态
        Integer apiStatus = interfaceInfo.getApiStatus();
        InterfaceInfoEnum apiStatusEnum = ApiStatusUtil.getApiStatus(apiStatus);
        //api请求类型
        Integer reqType = interfaceInfo.getReqType();
        InterfaceInfoEnum reqTypeEnum = ApiStatusUtil.getReqType(reqType);
        //参数配置类型
        Integer paramCfgType = interfaceInfo.getParamCfgType();
        InterfaceInfoEnum paramConfigTypeEnum = ApiStatusUtil.getParamConfigType(paramCfgType);
        //数据源类型
        Integer dataSrcType = interfaceInfo.getDataSrcType();
        DataSourceType datasourceType = ApiStatusUtil.getDatasourceType(dataSrcType);
        //提交状态
        Integer testStatus = interfaceInfo.getTestStatus();
        InterfaceInfoEnum testStatusEnum = ApiStatusUtil.getTestStatus(testStatus);
        //过期类型
        Integer cacheExpiryPeriod = interfaceInfo.getCacheExpiryPeriod();
        InterfaceInfoEnum cacheExpirePeriodEnum = ApiStatusUtil.getCacheExpirePeriod(cacheExpiryPeriod);
        //入参
        List<InputParam> inputParams = null;
        String inputParamsJson = interfaceInfo.getInputParams();
        if (StringUtils.isEmpty(inputParamsJson)) {
            inputParams = new ArrayList<>();
        } else {
            inputParams = JSONUtil.toList(inputParamsJson, InputParam.class);
        }
        //出参
        List<OutputParam> outputParams = null;
        String outputParamJson = interfaceInfo.getOutputParams();
        if (StringUtils.isEmpty(outputParamJson)) {
            outputParams = new ArrayList<>();
        } else {
            outputParams = JSONUtil.toList(outputParamJson, OutputParam.class);
        }
        ApiInfoVo infoVo = ApiInfoVo.builder()
                .id(interfaceInfo.getId())
                .createAt(interfaceInfo.getCreateAt())
                .updateAt(interfaceInfo.getUpdateAt())
                .createBy(interfaceInfo.getCreateBy())
                .createUsername(Objects.isNull(createUser) ? "" : createUser.getUserAccount())
                .updateBy(interfaceInfo.getUpdateBy())
                .updateUsername(Objects.isNull(updateUser) ? "" : updateUser.getUserAccount())
                .apiNameCn(interfaceInfo.getApiNameCn())
                .apiName(interfaceInfo.getApiName())
                .apiDesc(interfaceInfo.getApiDesc())
                .projectId(interfaceInfo.getProjectId())
                .projectName(project.getProjectName())
                .datasourceId(interfaceInfo.getDatasourceId())
                .datasourceName(datasource.getDataName())
                .databasesName(interfaceInfo.getDatabasesName())
                .tableName(interfaceInfo.getTableName())
                .reqLimit(interfaceInfo.getReqLimit())
                .apiType(apiTypeEnum.getDesc())
                .apiStatus(apiStatusEnum.getDesc())
                .inputParams(inputParams)
                .outputParams(outputParams)
                .reqType(reqTypeEnum.getDesc())
                .apiPath(interfaceInfo.getApiPath())
                .paramCfgType(paramConfigTypeEnum.getDesc())
                .allowPaging(Objects.equals(interfaceInfo.getAllowPaging(), 0) ? "不分页" : "分页")
                .respPageSize(interfaceInfo.getRespPageSize())
                .respJson(interfaceInfo.getRespJson())
//                .dataSrcType(datasourceType.getDataSourceName())
                .originalHost(interfaceInfo.getOriginalHost())
                .originalPath(interfaceInfo.getOriginalPath())
                .originalMethod(interfaceInfo.getOriginalMethod())
                .errorCodeListJson(interfaceInfo.getErrorCodeListJson())
                .errorRespJson(interfaceInfo.getErrorRespJson())
                .bodyDescJson(interfaceInfo.getBodyDescJson())
                .apiToken(interfaceInfo.getApiToken())
                .queryTimeOut(interfaceInfo.getQueryTimeOut())
                .testStatus(testStatusEnum.getDesc())
                .isCache(Objects.equals(interfaceInfo.getIsCache(), 0) ? "是" : "否")
                .cacheExpiryDateCron(cacheExpirePeriodEnum.getDesc())
                .cacheExpiryDateCron(interfaceInfo.getCacheExpiryDateCron())
                .executionFlowChart(interfaceInfo.getExecutionFlowChart())
                .isIgnoreSyntaxCheck(Objects.equals(0, interfaceInfo.getIsIgnoreSyntaxCheck()) ? "否" : "是")
                .orchestrationFlow(interfaceInfo.getOrchestrationFlow())
                .build();
        return ResultUtils.success(infoVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean apiOperation(Long id, String action){
        ApiCenterInterfaceInfo interfaceInfo = getById(id);
        if (Objects.isNull(interfaceInfo)) {
            throw new BizException(StatusCode.NO_SUCH_API);
        }
        if (ApiOperationEnum.ONLINE_INTERFACE.getAction().equals(action)) {
            interfaceInfo.setApiStatus(ApiOperationEnum.ONLINE_INTERFACE.getCoed());
            updateById(interfaceInfo);
            PoolConfig.UPDATE_TABLE_LIFECYCLE_POOL.execute(()->{
                apiBeObserver.notifyObservers(ApiOperationEnum.ONLINE_INTERFACE.getAction(),id);
            });
            return true;
        }
        if (ApiOperationEnum.OFFLINE_INTERFACE.getAction().equals(action)) {
            interfaceInfo.setApiStatus(ApiOperationEnum.OFFLINE_INTERFACE.getCoed());
            updateById(interfaceInfo);
            PoolConfig.UPDATE_TABLE_LIFECYCLE_POOL.execute(()->{
                apiBeObserver.notifyObservers(ApiOperationEnum.OFFLINE_INTERFACE.getAction(),id);
            });
            return true;
        }
        return false;
    }

    /**
     * 根据路径查询api
     *
     * @param path
     * @return
     */
    @Override
    public ApiCenterInterfaceInfo getByApiPath(String path) {
        ApiCenterInterfaceInfo interfaceInfo = apiCenterInterfaceInfoMapper.getByApiPath(path);
        if (Objects.isNull(interfaceInfo)) {
            throw new BizException(StatusCode.NO_SUCH_API);
        }
        return interfaceInfo;
    }

    /**
     * 初始化配置信息
     *
     * @return
     */
    @Override
    public BaseResponse<Boolean> initInterfaceConfig() {
        PoolConfig.RUN_SYNC_JOB_POOL.submit(() -> {
            RLock lock = redissonClient.getLock(Constants.INTERFACE_CONFIG_EXECUTE_LOCK);
            //循环吧数据放入redis -->每500条放入一次
            Map<String, ApiCenterInterfaceInfo> pathMap = new HashMap<>();
            try {
                if (lock.tryLock(0, -1, TimeUnit.MINUTES)) {
                    for (int current = 1; ; current++) {
                        Page<ApiCenterInterfaceInfo> page = new Page<>(current, SEARCH_BATH_SIZE);
                        List<ApiCenterInterfaceInfo> infos = apiCenterInterfaceInfoMapper.getAllInterfaceInfo(page).getRecords();
                        if (CollectionUtils.isEmpty(infos)) {
                            break;
                        }
                        infos.forEach((info) -> {
                            redisCache.set(Constants.INTERFACE_CONFIG_PRE + info.getId(), info);
                            pathMap.put(info.getApiPath(), info);
                        });
                    }

                    redisCache.set(Constants.INTERFACE_PATH_MAP, pathMap);

                }
            } catch (Exception e) {
                log.error("config to redis error:{}", e);
            } finally {
                //释放锁----->只释放自己的锁
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        });
        return ResultUtils.success(true);
    }
}
